#include "my_http_session_pool.h"
#include "comm/my_log.h"

#define MY_HTTP_SERVER_SESSION_POOL_TAG        "my_http_server_session_pool_tag"


static void
my_http_server_session_read_handler(my_event_t *event)
{
    my_conn_t                       *conn;
    my_http_server_session_pool_t   *pool;

    conn = event->data;

    pool = my_thread_store_get(MY_HTTP_SERVER_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    if (event->timedout) {
        event->timedout = 0;
        my_queue_remove(&conn->queue);
        pool->free_handler(conn);
        pool->current--;
        return;
    }

    my_event_loop_del_timer(event);
    my_event_loop_disabled_io(conn, MY_FALSE);
    my_queue_remove(&conn->queue);
    pool->current--;

    conn->read.handler = conn->server_handler;
    conn->read.handler(&conn->read);
}


void
my_http_server_session_pool_put(my_conn_t *conn, int64_t wait_timeout)
{
    my_queue_t                      *q;
    my_conn_t                       *last_conn;
    my_http_server_session_pool_t   *pool;

    pool = my_thread_store_get(MY_HTTP_SERVER_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    if (!conn->server_handler) {
        MY_LOG_ERROR("the connect server handler is nil");
        pool->free_handler(conn);
        return;
    }

    // release last server conn
    if (pool->current > 0 && pool->current >= pool->max_conns) {
        q = my_queue_last(&pool->head);
        last_conn = my_queue_data(q, my_conn_t, queue);
        my_queue_remove(q);
        pool->free_handler(last_conn);
        pool->current--;
    }

    conn->read.handler = my_http_server_session_read_handler;
    my_event_loop_add_timer(&conn->read, wait_timeout);
    my_event_loop_enabled_io(conn, MY_FALSE);
    my_queue_insert_head(&pool->head, &conn->queue);
    pool->current++;
}


void
my_http_server_conn_pool_release()
{
    my_queue_t                      *q;
    my_conn_t                       *last_conn;
    my_http_server_session_pool_t   *pool;

    pool = my_thread_store_get(MY_HTTP_SERVER_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    while (pool->current > 0) {
        q = my_queue_last(&pool->head);
        last_conn = my_queue_data(q, my_conn_t, queue);
        my_queue_remove(q);
        pool->free_handler(last_conn);
        pool->current--;
    }
}


///////////////////////////////////////////////////////////////////

#define MY_HTTP_CLIENT_SESSION_POOL_TAG         "my_http_client_session_pool_tag"


static void
my_http_client_session_read_handler(my_event_t *event)
{
    my_conn_t                       *conn;
    my_http_client_conn_data_t      *conn_data;
    my_http_client_session_pool_t   *pool;

    conn = event->data;
    conn_data = conn->data;

    if (conn_data == NULL) {
        MY_LOG_FATAL("unreachable");
    }

    pool = my_thread_store_get(MY_HTTP_CLIENT_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    if (!my_slru_remove(pool->slru, conn_data)) {
        MY_LOG_FATAL("unreachable");
    }
}


void
my_http_client_session_pool_put(my_str_t *key,
    my_conn_t *conn, int64_t wait_timeout)
{
    if (conn->data != NULL) {
        MY_LOG_FATAL("unexcept conn data");
    }

    my_http_client_conn_data_t      *conn_data;
    my_http_client_session_pool_t   *pool;

    pool = my_thread_store_get(MY_HTTP_CLIENT_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    conn_data = my_http_client_conn_data_create(key, conn, pool->free_handler, 1);
    conn->data = conn_data;

    conn->read.handler = my_http_client_session_read_handler;
    my_event_loop_add_timer(&conn->read, wait_timeout);
    my_event_loop_enabled_io(conn, MY_FALSE);
    my_slru_put(pool->slru, conn_data, conn_data);
}


my_conn_t *
my_http_client_session_get(my_str_t *key)
{
    my_http_client_session_pool_t   *pool;
    my_http_client_conn_data_t      *conn_data;
    my_conn_t                       *conn;

    pool = my_thread_store_get(MY_HTTP_CLIENT_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    my_http_client_conn_data_t      target = {
        .is_put = 0
    };

    target.key.data = key->data;
    target.key.len = key->len;

    conn_data = my_slru_get(pool->slru, &target, NULL);
    if (conn_data == NULL) {
        return NULL;
    }

    conn = conn_data->conn;

    my_event_loop_del_timer(&conn->read);
    my_event_loop_disabled_io(conn, MY_FALSE);

    return conn;
}


void
my_http_client_session_pool_release()
{
    my_http_client_session_pool_t   *pool;

    pool = my_thread_store_get(MY_HTTP_CLIENT_SESSION_POOL_TAG);
    if (!pool) {
        MY_LOG_FATAL("unreachable");
    }

    my_slru_destroy(pool->slru);
    pool->slru = NULL;
}